// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "generator/internal/mock_connection_generator.h"
#include "absl/memory/memory.h"
#include "generator/internal/codegen_utils.h"
#include "generator/internal/descriptor_utils.h"
#include "generator/internal/predicate_utils.h"
#include "generator/internal/printer.h"
#include <google/protobuf/descriptor.h>

namespace google {
namespace cloud {
namespace generator_internal {

MockConnectionGenerator::MockConnectionGenerator(
    google::protobuf::ServiceDescriptor const* service_descriptor,
    VarsDictionary service_vars,
    std::map<std::string, VarsDictionary> service_method_vars,
    google::protobuf::compiler::GeneratorContext* context)
    : ServiceCodeGenerator("mock_connection_header_path", service_descriptor,
                           std::move(service_vars),
                           std::move(service_method_vars), context) {}

Status MockConnectionGenerator::GenerateHeader() {
  HeaderPrint(CopyrightLicenseFileHeader());
  HeaderPrint(  // clang-format off
    "// Generated by the Codegen C++ plugin.\n"
    "// If you make any local changes, they will be lost.\n"
    "// source: $proto_file_name$\n"
    "\n"
    "#ifndef $header_include_guard$\n"
    "#define $header_include_guard$\n"
    "\n");
  // clang-format on

  // includes
  HeaderLocalIncludes({vars("connection_header_path")});
  HeaderSystemIncludes({"gmock/gmock.h"});
  HeaderPrint("\n");

  auto result = HeaderOpenNamespaces(NamespaceType::kMocks);
  if (!result.ok()) return result;

  // Abstract interface Connection base class
  HeaderPrint(  // clang-format off
    "class $mock_connection_class_name$ : public $product_namespace$::$connection_class_name$ {\n"
    " public:\n");
  // clang-format on

  for (auto const& method : methods()) {
    HeaderPrintMethod(
        method,
        {MethodPattern(
             {
                 {IsResponseTypeEmpty,
                  // clang-format off
    "  MOCK_METHOD(Status,\n",
    "  MOCK_METHOD(StatusOr<$response_type$>,\n"},
   {"  $method_name$,\n"
    "  ($request_type$ const& request), (override));\n\n",}
                 // clang-format on
             },
             All(IsNonStreaming, Not(IsLongrunningOperation),
                 Not(IsPaginated))),
         MethodPattern(
             {
                 {IsResponseTypeEmpty,
                  // clang-format off
    "  MOCK_METHOD(future<Status>,\n",
    "  MOCK_METHOD(future<StatusOr<$longrunning_deduced_response_type$>>,\n"},
   {"  $method_name$,\n"
    "  ($request_type$ const& request), (override));\n\n",}
                 // clang-format on
             },
             All(IsNonStreaming, IsLongrunningOperation, Not(IsPaginated))),
         MethodPattern(
             {
                 // clang-format off
   {"  MOCK_METHOD(StreamRange<$range_output_type$>,\n"
    "  $method_name$,\n"
    "  ($request_type$ request), (override));\n\n"},
                 // clang-format on
             },
             All(IsNonStreaming, Not(IsLongrunningOperation), IsPaginated)),
         MethodPattern(
             {
                 // clang-format off
   {"  MOCK_METHOD(StreamRange<$response_type$>,\n"
    "  $method_name$,\n"
    "  ($request_type$ const& request), (override));\n\n"},
                 // clang-format on
             },
             IsStreamingRead)},
        __FILE__, __LINE__);
  }

  for (auto const& method : async_methods()) {
    HeaderPrintMethod(
        method,
        {MethodPattern(
            {
                {IsResponseTypeEmpty,
                 // clang-format off
    "  MOCK_METHOD(future<Status>,\n",
    "  MOCK_METHOD(future<StatusOr<$response_type$>>,\n"},
   {"  Async$method_name$,\n"
    "  ($request_type$ const& request), (override));\n\n",}
                // clang-format on
            },
            All(IsNonStreaming, Not(IsLongrunningOperation),
                Not(IsPaginated)))},
        __FILE__, __LINE__);
  }

  // close abstract interface Connection base class
  HeaderPrint(  // clang-format off
    "};\n\n");
  // clang-format on

  HeaderCloseNamespaces();
  // close header guard
  HeaderPrint(  // clang-format off
    "#endif  // $header_include_guard$\n");
  // clang-format on
  return {};
}

Status MockConnectionGenerator::GenerateCc() { return {}; }

}  // namespace generator_internal
}  // namespace cloud
}  // namespace google
